/**
 * 
 */
package com.gr.staffpm.notes.push;

import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gr.staffpm.datatypes.Task;
import com.gr.staffpm.datatypes.User;
import com.gr.staffpm.notes.activity.ActivityMessage;
import com.gr.staffpm.notes.activity.listener.IActivityListener;


/**
 * @author Graham Rhodes 20 Feb 2011 22:52:22
 */
public class ActivityService implements IActivityService, Serializable {

    private static final long serialVersionUID = 1L;
    private static final ExecutorService executorService = Executors.newFixedThreadPool(5);
    private final Map<User, IActivityListener> listeners = new ConcurrentHashMap<User, IActivityListener>();
    private final Logger log = LoggerFactory.getLogger(ActivityService.class);
    private static ActivityService instance = null;

    protected ActivityService() {
        //Singleton
    }

    public static ActivityService getInstance() {
        if (instance == null)
            instance = new ActivityService();
        return instance;
    }

    /* (non-Javadoc)
     * @see com.gr.notes.activity.IActivityService#addListener(com.gr.notes.activity.listener.IActivityListener)
     */
    @Override
    public void addListener(User user, IActivityListener listener) {
        synchronized (listeners) {
            listeners.put(user, listener);
        }
    }

    /* (non-Javadoc)
     * @see com.gr.notes.activity.IActivityService#removeListener(com.gr.notes.activity.listener.IActivityListener)
     */
    @Override
    public void removeListener(User user) {
        synchronized (listeners) {
            listeners.remove(user);
        }
    }

    private void _notify(final IActivityListener listener, final ActivityMessage message) {
        try {
            listener.onMessage(message);
        } catch (final Exception ex) {
            log.error("Failed to notify activity listener " + listener, ex);
        }
    }

    /**
     * sends the activity message asynchronously (in a background thread) to a specific listener
     */
    public void sendAsyncUser(final User user, Task task, final DateTime timestamp, final String content) {
        final ActivityMessage msg = new ActivityMessage(task, timestamp, content);

        executorService.submit(new Runnable() {

            @Override
            public void run() {
                for (User userListener : listeners.keySet()) {
                    if (userListener.getUsername().equals(user.getUsername()))
                        _notify(listeners.get(userListener), msg);
                }
            }
        });
    }

    /**
     * sends the activity message asynchronously (in a background thread) to a specific listener
     */
    public void sendAsyncUser(final User user, final ActivityMessage msg) {

        executorService.submit(new Runnable() {

            @Override
            public void run() {
                for (User userListener : listeners.keySet()) {
                    if (userListener.getUsername().equals(user.getUsername())) {
                        _notify(listeners.get(userListener), msg);
                    }
                }
            }
        });
    }

}
